package com.config;

import java.util.Collection;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import com.bean.Menu;
import com.bean.Role;
import com.service.MenuService;

/****
 * @Description 要实现动态配置权限，首先需要自定义 FilterInvocationSecurityMetadataSource：
 * 自定义 FilterInvocationSecurityMetadataSource 主要实现该接口中的 getAttributes 方法，该方法用来确定一个请求需要哪些角色。
 *
 */
@Component
public class MyFilter implements FilterInvocationSecurityMetadataSource {
	/****
	 * 创建一个AntPathMatcher，主要用来实现ant风格的URL匹配。
	 */
    AntPathMatcher pathMatcher = new AntPathMatcher();
    @Autowired
    MenuService menuService;

    @Override
    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
    	// 从参数中提取出当前请求的URL
        String requestUrl = ((FilterInvocation) o).getRequestUrl();
        // 从数据库中获取所有的资源信息， 
        // 在真实项目环境中，开发者可以将资源信息缓存在Redis或者其他缓存数据库中。
        List<Menu> allMenus = menuService.getAllMenus();
        for (Menu menu : allMenus) {
        	 // 遍历资源信息，遍历过程中获取当前请求的URL所需要的角色信息并返回。
            if (pathMatcher.match(menu.getPattern(), requestUrl)) {
                List<Role> roles = menu.getRoles();
                String[] rolesStr = new String[roles.size()];
                for (int i = 0; i < roles.size(); i++) {
                    rolesStr[i] = roles.get(i).getName();
                }
                //把路径存储进来  在实现AccessDecisionManager的类中来进行判断路径
                return SecurityConfig.createList(rolesStr);
            }
        }
     // 如果当前请求的URL在资源表中不存在相应的模式，就假设该请求登录后即可访问，即直接返回 ROLE_LOGIN.
        return SecurityConfig.createList("ROLE_login");
    }

 // 该方法用来返回所有定义好的权限资源，Spring Security在启动时会校验相关配置是否正确。
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
    	// 如果不需要校验，那么该方法直接返回null即可。
        return null;
    }

    // supports方法返回类对象是否支持校验。
    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}
